/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.analyzer;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Comparator;

import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGParameterList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ImportMap;
import org.jfab.core.collection.MethodBodyList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.collection.ParameterNameList;
import org.jfab.core.factory.CGAnnotationFactory;
import org.jfab.core.factory.CGMethodFactory;
import org.jfab.core.factory.CGParameterFactory;
import org.jfab.core.factory.CGTypeFactory;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.model.CGMethod;
import org.jfab.core.model.CGMethodDeclaration;
import org.jfab.core.model.CGType;
import org.jfab.core.model.CGTypeUtilities;
import org.jfab.core.model.TypeFactory;

/**
 * Provides a method analyzer.
 */
public final class InterfaceMethodAnalyzer extends
        AbstractAnalyzer<CGMethodDeclaration, Method>
{
    /** Method analysis utilities. */
    private final MethodAnalysisUtilities _methodAnalysisUtilities;

    /** Method factory. */
    private final CGMethodFactory _methodFactory;

    /** Parameter factory. */
    private final CGParameterFactory _parameterFactory;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeFactory Type factory.
     * @param cgTypeUtils Code generator type utilities. (required)
     * @param cgTypeFactory Code generator type factory.
     * @param stringFactory String factory.
     * @param annotationFactory Annotation factory.
     * @param comparator Comparator.
     * @param parameterFactory Parameter factory.
     * @param methodAnalysisUtilities Method analysis utilities.
     * @param methodFactory Method factory.
     */
    public InterfaceMethodAnalyzer(final TypeFactory typeFactory,
            final CGTypeUtilities cgTypeUtils,
            final CGTypeFactory cgTypeFactory,
            final StringFactory stringFactory,
            final CGAnnotationFactory annotationFactory,
            final Comparator<CGMethodDeclaration> comparator,
            final CGParameterFactory parameterFactory,
            final MethodAnalysisUtilities methodAnalysisUtilities,
            final CGMethodFactory methodFactory)
    {
        super(typeFactory, cgTypeUtils, cgTypeFactory, stringFactory,
                annotationFactory, comparator);

        _parameterFactory = parameterFactory;
        _methodAnalysisUtilities = methodAnalysisUtilities;
        _methodFactory = methodFactory;
    }

    @Override
    public final CGMethod analyze(final Method method, final String javaSource)
    {
        Validate.notNull(method, "method is null");

        CGTypeList referencedTypes = null;
        CommentList commentLines = null;
        CGAnnotationList annotations = null;
        final ModifierList modifiers = filterModifiers(method.getModifiers());
        CGType returnType = getCgTypeFactory().createType(
                method.getReturnType());
        final String methodName = method.getName();
        final CGTypeList parameterTypes = createListType(method
                .getParameterTypes());
        ParameterNameList parameterNames = null;
        final CGTypeList throwsTypes = createListType(method
                .getGenericExceptionTypes());
        final MethodBodyList methodBodyLines = null;

        if (javaSource != null)
        {
            final ImportMap importMap = analyzeImports(javaSource);

            if (!importMap.isEmpty())
            {
                referencedTypes = new CGTypeList();
            }

            final boolean isInterface = true;
            final int methodIndex = locateMethod(javaSource, methodName,
                    parameterTypes, throwsTypes, isInterface);

            if (methodIndex >= 0)
            {
                final String preMethod = javaSource.substring(0, methodIndex);
                commentLines = parseCommentLines(preMethod);
                annotations = getAnnotations(preMethod);

                final MethodAnalysisUtilities methodUtils = getMethodAnalysisUtilities();
                final String packageName = parsePackageName(javaSource);
                returnType = methodUtils.parseReturnType(packageName,
                        importMap, javaSource, preMethod);

                final String postMethod = javaSource.substring(methodIndex
                        + methodName.length());
                parameterNames = methodUtils.parseParameterNames(postMethod);
            }
        }

        if (parameterNames == null)
        {
            parameterNames = new ParameterNameList();
        }

        final CGParameterList parameters = getParameterFactory()
                .createParameters(parameterTypes, parameterNames);

        return getMethodFactory().createMethod(referencedTypes, commentLines,
                annotations, modifiers, returnType, methodName, parameters,
                throwsTypes, methodBodyLines);
    }

    /**
     * @param modifiers Modifiers.
     * 
     * @return the given parameter filtered for particular modifiers.
     */
    private ModifierList filterModifiers(final int modifiers)
    {
        int value = modifiers;

        if (Modifier.isPublic(value))
        {
            value -= Modifier.PUBLIC;
        }

        if (Modifier.isAbstract(value))
        {
            value -= Modifier.ABSTRACT;
        }

        return new ModifierList(value);
    }

    /**
     * @return the methodAnalysisUtilities
     */
    private MethodAnalysisUtilities getMethodAnalysisUtilities()
    {
        return _methodAnalysisUtilities;
    }

    /**
     * @return the methodFactory
     */
    private CGMethodFactory getMethodFactory()
    {
        return _methodFactory;
    }

    /**
     * @return the parameterFactory
     */
    private CGParameterFactory getParameterFactory()
    {
        return _parameterFactory;
    }
}
